#_*_coding:UTF-8 _*_
from __future__ import  with_statement
import  copy
import  xml.etree.cElementTree as ET
import math
import os
import pandas as pd
import shutil
import re
import time
import numpy as np
from concurrent.futures import ThreadPoolExecutor
import concurrent.futures as cf
import ConfigParser



class DataCleaning:
    '''
    对原始轨迹做一些预处理
    @:param fileName:某一个摄像头下的所有顾客轨迹数据
    '''
    def  __init__(self):
        self.fileName=None
    def  __init__(self,fileName):
        self.fileName=fileName

    def getFileName(self):
        return  self.fileName

    def setFileName(self,value):
        self.fileName=value

    def getFile(self):
        '''
        取得文件夹下的每个文件的路径
        :return: list
        '''
        return  os.listdir(self.fileName)

    def overlopArea(self,car,consumer):
        '''
        计算车和人的矩形框的重叠面积
        :param car:list（x,y,width,height）车的矩形框的属性，分别是矩形框左上角的坐标x，y
        和矩形框的长宽width，height
        :param consumer:list（x,y,width,height），各个值的关系和car的类似
        :return:（float）顾客和车重叠的面积占顾客总面积的比率
        '''

        #初始化车的坐标
        x1, y1, width1, height1=[0,0,0,0]
        x2, y2, width2, height2 = [0,0,0,0]
        if  len(car)==4:
            x1,y1,width1,height1=car
        else:
            print "车的位置坐标有误!"

        #初始化顾客的坐标
        if  len(consumer)==4:
            x2,y2,width2,height2=consumer
        else:
            print "顾客的位置坐标有误!"

        endx=max(float(x1)+float(width1),float(x2)+float(width2))
        startx=min(float(x1),float(x2))
        width=float(width1)+float(width2)-(endx-startx)

        endy=max(float(y1)+float(height1),float(y2)+float(height2))
        starty=min(float(y1),float(y2))
        height=float(height2)+float(height1)-(endy-starty)

        ratio=0.0
        if  width<=0.0 or height<=0.0:
            return ratio
        else:
            area=width*height
            areaCon=float(width2)*float(height2)
            ratio=area/areaCon

        if  ratio>1.000000001:
            print  "算法有误！"
            return  0.0
        return ratio

    def consumerTrack(self,path):
        '''
        从原始轨迹中提取出每个每个顾客的轨迹（空间坐标）
        :param path:原始轨迹的路径（string）
        :param result:提取的顾客的轨迹的路径（string）
        :return: id映射关系
        '''
        #在这个方法中需要添加提取真实id（用list存储）和真实id和临时id之间的对应关系（用dict存储），数据结构为{cameraID：{tempID：realID}}
        #读csv

        # if os.path.exists(conResult):
        #     dirs=os.listdir(conResult)
        #     for d in dirs:
        #         if os.path.isdir(d):
        #             shutil.rmtree(d)
        #     # os.makedirs(conResult)
        #     shutil.rmtree(conResult)
        #
        # if os.path.exists(saleResult):
        #     dirs = os.listdir(saleResult)
        #     for d in dirs:
        #         if os.path.isdir(d):
        #             shutil.rmtree(d)
        #     # os.mkdir(saleResult)
        #     shutil.rmtree(saleResult)

        data=pd.read_csv(path)
        # print data.head()

        #1. 多线程的获取各个映射关系
        # task_result=[]
        # with ThreadPoolExecutor(5) as ex:
        #     task=[]
        #     task.append(ex.submit(self.tmpMapTrue, data))
        #     task.append(ex.submit(self.frameTmp, data))
        #     task.append(ex.submit(self.timeTmp,data))
        #     task.append(ex.submit(self.getRealId,data))
        #     task.append(ex.submit(self.getSaleId,data))
        #
        #     for t in task:
        #         try:
        #             result=t.result()
        #             if result is not None:
        #                 task_result.append(result)
        #         except Exception as e:
        #             t.cancel()
        #             print e
        # idMap, startInShop, startTime, realId, sale = task_result


       #2. 单线程来获取映射关系
        # tmpidTrueid = data[data.is_customer == 1]
        # tmpidTrueid = tmpidTrueid[['tmp_id', 'true_id']].drop_duplicates(['tmp_id'])  # 临时ID和真实ID之间映射的DataFrame
        # tmpidTrueid = tmpidTrueid.fillna("")
        # # tmpidTrueid = tmpidTrueid
        # # tmpidTrueid=tmpidTrueid.drop_duplicates(['tmp_id'])
        # key = tmpidTrueid['tmp_id'].values
        # key = map(str, key)
        # values = tmpidTrueid['true_id'].values
        # idMap = dict(zip(key, values))#存放临时id和真实id之间的映射

        tmpidTrueid=data.loc[lambda df:df.is_customer==1,lambda  df:['tmp_id','true_id']].drop_duplicates('tmp_id').fillna("")
        idMap=dict(zip(map(str,tmpidTrueid.tmp_id), map(str,tmpidTrueid.true_id)))#存放临时id和真实id之间的映射

        # data.loc[lambda df: df.is_customer==1, 'frame', 'tmp_di']
        # frameTmpidDF = data.loc[(data["is_customer"] == 1), ("frame", "tmp_id")].drop_duplicates(['tmp_id'])  # 临时ID和帧数之间的映射DataFrame
        # keys = frameTmpidDF['tmp_id'].values
        # values = frameTmpidDF['frame'].values
        # # startInShop = {}  # 临时id：进店帧数
        # startInShop = dict(zip(keys, values))#临时id：进店帧数
        # data.to_dict()
        frameTmpidDF=data.loc[lambda df:df.is_customer==1,lambda  df:['frame','tmp_id','unix_time']].drop_duplicates('tmp_id')  # 临时ID和帧数之间的映射DataFrame
        startInShop = dict(zip(map(str,frameTmpidDF.tmp_id), map(str,frameTmpidDF.frame)))  # 临时id：进店帧数

        # timeTmpidDF = data.loc[lambda df: df.is_customer==1,lambda df:["unix_time", "tmp_id"]].drop_duplicates('tmp_id')  # 临时ID和时间戳之间的映射DataFrame
        # keys = timeTmpidDF['tmp_id'].values
        # values = timeTmpidDF['unix_time'].values
        # startTime = {}  # 临时id：进店时间戳
        startTime = dict(zip(map(str,frameTmpidDF.tmp_id), map(str,frameTmpidDF.unix_time)))#临时id：进店时间戳

        consumerID = data.loc[lambda df: df.is_customer==1,lambda df:["true_id"]].drop_duplicates().dropna(subset=['true_id'])  # 真实IDDataFrame
        # consumerID = consumerID.dropna()
        # consumerID = consumerID.values
        realId =map(lambda x: x[0],np.array(consumerID).tolist())#存放真实id
        # realId = list(consumerID.values) # 存放真实id

        saleID = data.loc[lambda df:df.is_customer==2,lambda df:["tmp_id"]].drop_duplicates() # 临时IDDataFrame
        # saleID = saleID.values
        sale = map(str,map(lambda x:x[0],np.array(saleID).tolist()))# 存放销售的临时ID

        #总体来说，用多线程实现时，因为每个映射本身的时间不长，但是多线程还要启动线程池，所以可能多线程并不会有多大优势
        data=data[['frame','tmp_id','X_min','Y_min','w','h','is_customer']]

        return realId, idMap, startInShop,startTime,sale,data


    def tmpMapTrue(self,data):
        '''
        从原始数据中获取临时ID和真实id之间的映射
        :param data:DataFrame
        :return:（dict）临时ID和真实ID之间的映射关系
        '''
        tmpidTrueid = data[data.is_customer == 1]
        tmpidTrueid = tmpidTrueid[['tmp_id', 'true_id']].drop_duplicates(['tmp_id'])  # 临时ID和真实ID之间映射的DataFrame
        tmpidTrueid = tmpidTrueid.fillna("")
        # tmpidTrueid = tmpidTrueid
        # tmpidTrueid=tmpidTrueid.drop_duplicates(['tmp_id'])
        key=tmpidTrueid['tmp_id'].values
        key=map(str,key)
        values=tmpidTrueid['true_id'].values
        idMap=dict(zip(key,values))

        return idMap

    def frameTmp(self,data):
        """
        获取临时id和进门帧数之间的映射
        :param data:
        :return:
        """
        frameTmpidDF = data.loc[(data["is_customer"] == 1), ("frame", "tmp_id")].drop_duplicates(
            ['tmp_id'])  # 临时ID和帧数之间的映射DataFrame
        # frameTmpidDF = frameTmpidDF.values
        keys=frameTmpidDF['tmp_id'].values
        values=frameTmpidDF['frame'].values
        # startInShop = {}  # 临时id：进店帧数
        startInShop=dict(zip(keys,values))

        return  startInShop

    def timeTmp(self,data):
        '''
        获取进门时间戳和临时id之间的也射关系
        :param data:
        :return:
        '''
        timeTmpidDF = data.loc[(data["is_customer"] == 1), ("unix_time", "tmp_id")].drop_duplicates(
            ['tmp_id'])  # 临时ID和时间戳之间的映射DataFrame
        # timeTmpidDF = timeTmpidDF.values
        keys=timeTmpidDF['tmp_id'].values
        values=timeTmpidDF['unix_time'].values
        # startTime = {}  # 临时id：进店时间戳
        startTime=dict(zip(keys,values))

        return startTime

    def getRealId(self,data):
        '''
        获取顾客的真实ID
        :param data:
        :return:
        '''
        consumerID = data.loc[(data["is_customer"] == 1), ("true_id")].drop_duplicates()  # 真实IDDataFrame
        consumerID = consumerID.dropna()
        consumerID = consumerID.values
        realId = list(set(consumerID))
        return  realId

    def getSaleId(self,path):
        '''
        获取销售ID
        :param data:
        :return:
        '''
        data=pd.read_csv(path)
        saleID = data.loc[lambda df:df.is_customer==2, lambda df:["true_id"]].drop_duplicates()  # 临时IDDataFrame
        # saleID = saleID.values

        sale = map(lambda x: x[0], np.array(saleID).tolist())
        return  sale

    def  subTrack(self,data,start,end):
        '''
         从顾客的全轨迹中提取出特定帧数的轨迹
        :param data:（DataFrame）需要提取轨迹的顾客的全轨迹位置
        :param start:（string）开始的帧数
        :param end:(string)结束的帧数
        :return:（list）该段轨迹
        '''

        tmpData=  data[(data.frame>=float(start))& (data.frame<=float(end))]
        return tmpData.values


    def mergeIdMap(self,allIdMap,tempIdMap):
        '''
        合并多个摄像头下的顾客也舍
        :param allIdMap:（dict）ID映射关系,从原始文件中读出
        :param tempIdMap:(dict)ID映射关系，拼接得到
        :return:（dict)master：ipMap
        '''
        #合并id映射关系
        alm=copy.deepcopy(allIdMap)
        for camerykey in allIdMap:
            tmpALM=allIdMap[camerykey]
            if tempIdMap.has_key(camerykey):
                tIdMap=tempIdMap[camerykey]
            else:
                tIdMap={}
            for tempID in tIdMap:
                if  not tmpALM.has_key(tempID):
                    alm[camerykey][tempID]=tIdMap[tempID]#对映射做整合

        return alm


    def getCarCoord(self, xmlFileName):
        '''
        从xml文件中解析出车辆的坐标
        :param xmlFileName:（string）xml文件的路径
        :return:（dict）车辆的坐标 {carID：[x,y,width,height]}
        '''
        tree = ET.parse(xmlFileName)
        root = tree.getroot()
        fachilds = root.findall('object')
        carCoord = {}#车辆id和车辆坐标之间的映射
        carsID=[]#车辆ID集合
        for objects in fachilds:
            # rank=objects.find('rank').text
            carID = objects.find('name').text
            carsID.append(carID)
            childs = objects.findall('bndbox')
            coord = ""
            matrix=[[child.find('xmin').text,child.find('ymin').text,child.find('xmax').text,child.find('ymax').text] for child in childs]
            xmin,ymin,xmax,ymax=map(float,matrix[0])

            # for child in childs:
            #     xmin = child.find('xmin').text#x
            #     ymin = child.find('ymin').text#y
            #     xmax = child.find('xmax').text
            #     ymax = child.find('ymax').text
            width = xmax - xmin#宽
            height = ymax - ymin#高
            coord =  str(xmin) + "," + str(ymin) + "," + str(width) + "," + str(height)
            carCoord[carID] = coord

        return carCoord,carsID

    def euclideanDistances(self,x1,y1,x2,y2):
        '''
        计算两点之间的欧式距离
        :param x1: （str）横坐标
        :param y1: （str）纵坐标
        :param x2: （str）横坐标
        :param y2: （str）纵坐标
        :return: （float）两点之间的欧式距离
        '''
        return math.sqrt(math.pow(float(x1)-float(x2),2)+math.pow(float(y1)-float(y2),2))

    def mergePotentialCon(self,potentialCon,allIdMap,tempIdMap):
        '''
        合并各个摄像头下的潜在客户
        :param potentialCon:（dict）各个摄像头下的潜在客户
        :param allIdMap:（dict）ID映射关系,从原始文件中读出
        :param tempIdMap:(dict)ID映射关系，拼接得到
        :return:(list)潜在客户
        '''
        # 合并id映射关系
        alm = copy.deepcopy(allIdMap)
        for camerykey in allIdMap:
            tmpALM = allIdMap[camerykey]
            if tempIdMap.has_key(camerykey):
                tIdMap = tempIdMap[camerykey]
            else:
                tIdMap={}
            for tempID in tIdMap:
                if not tmpALM.has_key(tempID):
                    alm[camerykey][tempID] = tIdMap[tempID]
        mergePoten={}
        for  camerakey in potentialCon:
            idMap=alm[camerakey]
            aloneConsumer=potentialCon[camerakey]
            for con in aloneConsumer:
                #如果顾客只出现在了一个摄像头下
                if idMap[con.split(",")[0]] and idMap[con.split(",")[0]] not in mergePoten:
                    mergePoten[idMap[con.split(",")[0]]]=con.split(",")[1]

                #如果顾客在多个摄像头下出现，则需要对购车意向度进行叠加
                elif idMap[con.split(",")[0]] and idMap[con.split(",")[0]] in mergePoten:
                    mergePoten[idMap[con.split(",")[0]]] = str(max(float(mergePoten[idMap[con.split(",")[0]]]) ,float(con.split(",")[1]))+0.05)

        return  mergePoten

    def mergeFamily(self,family,allIdMap,tempIdMap):
        '''
        合并多个摄像头下的集体客户
        :param family:（dict）存放多个摄像头下的“一家人”顾客数据{摄像头ID：{master：[一家人]}}
        :param allIdMap:（dict）ID映射关系,从原始文件中读出
        :param tempIdMap:(dict)ID映射关系，拼接得到
        :return:（dict)master：family
        '''
        #合并id映射关系（原始存在的和拼接成功的）
        alm=copy.deepcopy(allIdMap)
        for camerykey in allIdMap:
            tmpALM=allIdMap[camerykey]
            if tempIdMap.has_key(camerykey):
                tIdMap=tempIdMap[camerykey]
            else:
                tIdMap={}
            for tempID in tIdMap:
                if  not tmpALM.has_key(tempID):
                    alm[camerykey][tempID]=tIdMap[tempID]#对映射做整合

        allFamily={}
        for camerykey in family:
            idMap=alm[camerykey]
            aloneFamily=family[camerykey]
            for master in aloneFamily:
                slaves =aloneFamily[master]
                if idMap[master]!="":#如果master有对应的真实ID
                    temp=[]
                    newMaster=idMap[master]
                    for slave in  slaves:
                        if idMap[slave.split(",")[0]]!="" and   idMap[slave.split(",")[0]] not  in temp:
                            temp.append(idMap[slave.split(",")[0]])
                    if len(temp)>=2 :
                        if not allFamily.has_key(newMaster):
                            allFamily[newMaster]=temp
                        else:
                            allFamily[newMaster].extend(temp)
                else:#如果master没有对应的真实ID，用有真实Id的slave来代替原先的master
                    temp = []
                    for slave in slaves:
                        if idMap[slave.split(",")[0]] != "":
                            newMaster=idMap[slave.split(",")[0]]
                            if idMap[slave.split(",")[0]] not in temp:
                                temp.append(idMap[slave.split(",")[0]])
                    if len(temp)>=2 :
                        if not allFamily.has_key(newMaster):
                            allFamily[newMaster]=temp
                        else:
                            allFamily[newMaster].extend(temp)

        for f in allFamily:#去重
            allFamily[f]=list(set(allFamily[f]))

        return allFamily,alm

    def mapFamily(self,allFamily):
        '''
        把团体客户映射成slave：master，以供写表的时候调用
        :param allFamily:（dict）集体客户映射关系》master：slaves（list）
        :return:（dict）slave：master
        '''
        slaveMaster={}
        for master in allFamily:
            for slave in allFamily[master]:
                slaveMaster[slave]=master
        return  slaveMaster

    def mergeRealId(self,allRealId):
        '''
        对多个摄像头的顾客trueID进行合并
        :param allRealId:（dict）多个摄像头的顾客id映射
        :return:（list）真实ID
        '''
        realID=[]
        for camerakey in allRealId:
            for id in allRealId[camerakey]:
                if id not in realID:
                    realID.append(allRealId[camerakey])
        return  realID


    def getDataPath(self,dataPath):
        '''
        从原始文件总路径中中解析出各个摄像头文件的路径，得到各个摄像头下销售和顾客的轨迹存放路径
        :param dataPath:（string）一家4s店数据的总路径
        :return:（dict）allTrack{摄像头ID：文件路径}，shopID：店铺ID，videoTime：视频时间
        '''
        allTrack={}#不同摄像头下原始轨迹的存放路径

        files=os.listdir(dataPath)
        line =dataPath.strip(" ").split(os.path.sep)
        pattern="\d{8}"#8位数字的正则表达式
        shopId="0"#店铺ID
        videoTime="0"#视屏时间（处理的是哪天的视频）
        for i in line:
            if i.startswith("SQ"):
                shopId=i#店铺ID
            if re.match(pattern,i):
                videoTime=re.match(pattern,i).group()#视频日期

        for oneCamera in files:#得到各个摄像头下文件的存放路径
            if os.path.isfile(os.path.join(dataPath,oneCamera)):
                if not oneCamera.startswith("track_match_Channel_") :#匹配摄像头下的轨迹文件
                    continue
                # if oneCamera.endswith(cameraInFace+".csv"):
                #     continue
                camerakeytemp=oneCamera.replace(".csv","").split("_")
                camerakey=camerakeytemp[-2]+"_"+camerakeytemp[-1]

                allTrack[camerakey]=os.path.join(dataPath,oneCamera)


        return allTrack,shopId,videoTime

    def getCarLocation(self,locationPath):
        '''
        获取不同摄像头下车辆位置文件
        :param locationPath:（string）一家4s店中不同摄像头下的车辆位置xml文件合集
        :return:（dict）carCoord{摄像头ID：路径}
        '''
        #carCoord={}#存放不同摄像头下车辆位置xml文件的路径
        doorCoord=""#存放门口位置的路劲
        files=os.listdir(locationPath)
        for xml in files:
            camerakey=xml.replace(".xml","")
            #if camerakey.startswith("Chann"):
            #    carCoord[camerakey]=os.path.join(locationPath,xml)
            if camerakey.startswith("door"):
                doorCoord=os.path.join(locationPath,xml)
        #return  carCoord,doorCoord
        return  doorCoord

    def getDBPara(self,dbPara):
        '''
        从输入的参数中获取链接数据库的参数
        :param dbPara:（string）数据库参数字符串
        :return:（list）链接数据库的各个参数
        '''
        line=dbPara.strip(" ").split(",")
        para=[]
        for p in line:
            para.append(p)
        return para

    def getTime(self,frame,startTime,startInShop):
        '''
        得到某个顾客指定帧数的时间戳
        :param frame:（string）帧数
        :param startTime:（string）{顾客真实ID进店时的时间戳}
        :param startInShop:（string）{顾客真实ID进店时的帧数}
        :return:（string）帧数对应的时间戳
        '''
        dis=float(frame)-float(startInShop)
        disTime=dis*0.25
        timestamp=int(startTime)+int(disTime)
        return  str(timestamp)

    def getUnixTime(self,date):
        '''
        获取unix时间戳
        :param date:时间（例如：20171022）
        :return:时间戳
        '''
        ymd=list(date)
        if len(ymd)!=8:
            print "时间格式有误！"
            sys.stderr()
        y="".join(ymd[0:4])
        m="".join(ymd[4:6])
        d="".join(ymd[6:])
        dt=y+"-"+m+"-"+d
        timeArray = time.strptime(dt, "%Y-%m-%d")
        # 转换成时间戳
        timestamp = time.mktime(timeArray)
        return int(timestamp)

    def getConfigInfo(self,configPath):
        '''
        从配置文件中读取算法相关配置参数
        :param configPath:（string）配置文件路径
        :return:xml路径，数据库参数，门口摄像头ID，人脸摄像头ID
        '''
        config = ConfigParser.ConfigParser()
        #解析配置文件
        with open(configPath, 'r') as cfgfile:
            config.readfp(cfgfile)
            #xmlPath = config.get('xmlPath', 'path')
            host = config.get('dbParam', 'host')
            port = config.get('dbParam', 'port')
            user = config.get('dbParam', 'user')
            password = config.get('dbParam', 'password')
            tableName = config.get('dbParam', 'tableName')
            cameraInDoor = config.get('cameraInDoor', 'cameraID')
            #cameraInFace = config.get('cameraInFace', 'cameraID')

        #return xmlPath,[host,port,user,password,tableName],cameraInFace,cameraInDoor
        #return xmlPath,[host,port,user,password,tableName],cameraInDoor
        return [host,port,user,password,tableName],cameraInDoor


if __name__=="__main__":
    import sys
    import os

    dc = DataCleaning("f://")
    # print(len(sys.argv))
    # if len(sys.argv) != 3:
    #     print("请输入正确的参数")
    #     raise IOError
    #
    #     #参数1是存放原始轨迹的文件夹，参数2是存放顾客轨迹的文件夹··
    # trackFile,resultFile=sys.argv[1:]
    # dc=DataCleaning(trackFile)
    # dirs=os.listdir(trackFile)
    # #把所有的原始轨迹存放在一个文件夹中，文件名是摄像头ID
    # allRealId={}#不同摄像头下的真实ID
    # allIdMap={}#不同摄像头下的ID映射关系
    # for dir in dirs:
    #     cameraID=dir.replace(".txt","")
    #     inputPath=os.path.join(trackFile,dir)
    #     outputPath=os.path.join(resultFile,cameraID)
    #     os.makedirs(outputPath)
    #     realID,idMap=dc.consumerTrack(inputPath,outputPath)
    #     allIdMap[cameraID]=realID
    #     allIdMap[cameraID]=idMap

    d="20171022"
    print dc.getUnixTime(d)








